/**
 * 
 */
package org.statewalker.parser.base;

import java.util.ArrayList;
import java.util.List;

import org.statewalker.dict.BaseDict;
import org.statewalker.dict.IToken;
import org.statewalker.tokenizer.CharStream;
import org.statewalker.tokenizer.ITokenizer;
import org.statewalker.tokenizer.StreamToken;
import org.statewalker.tokenizer.CharStream.Pointer;
import org.statewalker.validator.IValidatorListener;
import org.statewalker.validator.Validator;
import org.statewalker.validator.ValidatorFactory;
import org.statewalker.validator.ValidatorState;

public class Parser {

    private class TokenizerListener implements IValidatorListener, ITokenizer {

        private List<ITokenizer> fStack = new ArrayList<ITokenizer>();

        public void onActivate(
            ValidatorState parent,
            ValidatorState state,
            IToken token) {
            String key = state.getKey();
            ITokenizer tokenizer = fTokenizerProvider.getStateTokenizer(key);
            fStack.add(tokenizer);
        }

        public void onDeactivate(
            ValidatorState parent,
            ValidatorState state,
            IToken token) {
            fStack.remove(fStack.size() - 1);
        }

        public void onTransition(
            ValidatorState parent,
            ValidatorState prev,
            IToken event,
            ValidatorState next) {
        }

        /**
         * @see org.statewalker.tokenizer.ITokenizer#read(org.statewalker.tokenizer.CharStream)
         */
        public StreamToken read(CharStream stream) {
            StreamToken result = null;
            for (int i = fStack.size() - 1; result == null && i >= 0; i--) {
                ITokenizer tokenizer = fStack.get(i);
                if (tokenizer == null)
                    continue;
                result = tokenizer.read(stream);
            }
            return result;
        }
    }

    private boolean fFinished;

    private CharStream fStream;

    private StreamToken fToken;

    private TokenizerListener fTokenizerListener = new TokenizerListener();

    private ITokenizerProvider fTokenizerProvider;

    private Validator fValidator;

    public Parser(
        Validator validator,
        ITokenizerProvider tokenizerProvider,
        CharStream stream,
        IValidatorListener... listeners) {
        fValidator = validator;
        fTokenizerProvider = tokenizerProvider;
        fStream = stream;
        fValidator.addListeners(fTokenizerListener);
        fValidator.addListeners(listeners);
    }

    public Parser(
        ValidatorFactory factory,
        ITokenizerProvider tokenizerProvider,
        CharStream stream,
        IValidatorListener... listeners) {
        this(factory.newValidator(), tokenizerProvider, stream, listeners);
    }

    public void addListeners(IValidatorListener... listeners) {
        fValidator.addListeners(listeners);
    }

    public void close() {
        fToken = newEmptyToken(fStream, BaseDict.FINAL);
        fValidator.close(fToken);
    }

    protected ValidatorState getCurrentState() {
        return fValidator.getCurrentState();
    }

    public StreamToken loadNext() {
        if (fFinished)
            return null;
        if (fToken == null) {
            fToken = newEmptyToken(fStream, BaseDict.ROOT);
        } else {
            fToken = fTokenizerListener.read(fStream);
        }
        fFinished = fToken == null;
        if (fFinished)
            return null;
        updateValidator(fToken);
        return fToken;
    }

    private StreamToken newEmptyToken(CharStream stream, String k) {
        Pointer ptr = stream.getPointer();
        StreamToken token = new StreamToken(k, ptr, ptr, "");
        return token;
    }

    public void removeListeners(IValidatorListener... listeners) {
        fValidator.removeListeners(listeners);
    }

    protected void updateValidator(StreamToken token) {
        fValidator.next(token);
    }

}